package com.designPattern.singleton;

/**
 * @author mxs
 * 【单例模式】
 * 【饿汉式】的三种写法
 *
 * 【第一种方法】由于线程不安全：在这里不展示(其实和第二种方法类似，但是在方法声明中没有家synchronized)
 *
 * 【第二种方法】【线程安全】
 * 直接在声明方法的时候添加synchronized
 * 【优点】线程安全，可以在用到这个方法的时候才创建这个对象(饿汉式单例模式的优点)
 * 【缺点】每一次判断是否有这个对象都要进入同步代码块，执行效率比较低
 * 【重要】在开发中【不会使用】
 *
 * 【第三种写法】【线程并不安全，在main中演示类为什么不安全】
 * 【优点】不用在每一次获取对象的时候都进入同步代码块，执行效率比较高
 * 【缺点】线程并不安全，在实际开发中并不会使用,因为第一个在判断instance == null的时候，如果第一个线程还没有创建好对象第二个线程就同事也进入了
 * 判断instance == null中，就会造成初始化了多个对象，导致线程不安全的问题
 *
 * 【总结】三种创建方法的缺点都很致命
 * 第一种线程安全问题，第二种获取对象太慢，第三种线程不安全，所以在实际开发中并【不会使用】【懒汉式】的单例模式
 */
public class Type3 {
    public static void main(String[] args) {
        //在这里创建多个线程去创建Instance对象，使用第三种方式创建对象
        //会出现线程安全问题
        for (int i = 0; i < 10; i++) {
            new Thread() {
                @Override
                public void run() {
                    Instance instance = Singleton3.getInstance();
                    System.out.println(Thread.currentThread().getName() + ":" + instance.hashCode());
                }
            }.start();
        }
        //这里使用第二种方式创建对象
        //不会出现安全问题
        for (int i = 0; i < 10; i++) {
            new Thread() {
                @Override
                public void run() {
                    Instance instance = Singleton3.getInstance2();
                    System.out.println("+++"+Thread.currentThread().getName() + ":" + instance.hashCode());
                }
            }.start();
        }

    }
}

class Singleton3 {
    //私有化构造器，方位外部new
    private Singleton3() {
    }
    private static Instance instance;
    //第二种懒汉式的单例模式
    //在方法中声明同步
    public static synchronized Instance getInstance2() {
        if (instance == null) {
            instance = new Instance();
        }
        return instance;
    }
    //第三种懒汉式的单例模式
    //在方法中添加同步代码块
    public static Instance getInstance() {
        if (instance == null) {
            try {
                //在这里将使用Thread.sleep()，增加出现线程安全问题的概率
                //原理是让更多线程可以进入到这里，然后可以创建多个对象【instance】
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (Singleton3.class) {
                instance = new Instance();
            }
        }
        return instance;
    }

}
